﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IoTCM
{
    class SessionService
    {
        private UserService m_userService;
        private Dictionary<string, Session> m_sessions;
        private object m_sessionLock = new object();

        public TimeSpan SessionTimeout { get; set; }

        public SessionService()
        {
            m_userService = new UserService();
            m_sessions = new Dictionary<string, Session>();

            // a default session timeout of 5 minutes
            SessionTimeout = new TimeSpan(0, 5, 0);
        }

        public Session StartSession(string user, string password)
        {
            // first validate the user
            var rights = m_userService.GetUserRights(user, password);

            if (rights == UserRights.None)
            {
                // user is not authorized, they get no session
                return null;
            }

            // do they already have a session?

            lock (m_sessionLock)
            {
                var existing = m_sessions.Values.FirstOrDefault(s => s.User == user);
                if (existing != null)
                {
                    // update the access time
                    existing.LastAccess = DateTime.Now;

                    return existing;
                }
                else
                {
                    // generate a new session
                    var session = new Session(user, rights);

                    // store the session
                    m_sessions.Add(session.Token, session);

                    // return it
                    return session;
                }
            }
        }

        private void SessionTimeoutCleanupProc(object state)
        {
            while (true)
            {
                var now = DateTime.Now;

                lock (m_sessionLock)
                {
                    var tokens = m_sessions.Keys.ToArray();


                    foreach (var key in tokens)
                    {
                        var session = m_sessions[key];

                        // has the session timed out?
                        var et = now - session.LastAccess;
                        if (et > SessionTimeout)
                        {
                            // session is no longer valid, delete it
                            DeleteSession(key);
                        }
                    }
                }
            }
        }

        public void DeleteSession(string token)
        {
            lock (m_sessionLock)
            {
                if (m_sessions.ContainsKey(token))
                {
                    m_sessions.Remove(token);
                }
            }
        }

        public bool ValidateSession(string token, out Session session)
        {
            session = null;

            lock (m_sessionLock)
            {
                // does the session token even exist
                if (!m_sessions.ContainsKey(token))
                {
                    return false;
                }

                var now = DateTime.Now;
                session = m_sessions[token];

                // has the session timed out?
                var et = now - session.LastAccess;
                if (et > SessionTimeout)
                {
                    // session is no longer valid, delete it while we're here
                    DeleteSession(token);

                    return false;
                }

                // update the session access time
                session.LastAccess = now;

                return true;
            }
        }
    }
}
